package com.magenta.maxoptra.gis.distancematrix.builder;

import java.util.*;

/**
 * Collection of statistics of usage by geo-providers
 * <p/>
 * Not thread-safe
 *
 * @param <T> type of provider
 * @author Sergey Grachev
 */
final class GeoProviderUsage<T> {
    private final Map<T, Statistics> statistics = new HashMap<T, Statistics>();
    private final List<T> providers = new ArrayList<T>();
    private final Comparator<T> comparator = new Comparator<T>() {
        //        @Override
        public int compare(final T o1, final T o2) {
            return statistics.get(o1).busyHits - statistics.get(o2).busyHits;
        }
    };

    private Statistics ensureExists(final T provider) {
        final Statistics stat;
        if (!statistics.containsKey(provider)) {
            statistics.put(provider, stat = new Statistics());
            providers.add(provider);
        } else {
            stat = statistics.get(provider);
        }
        return stat;
    }

    /**
     * Add new provider (optional)
     *
     * @param provider as is
     */
    public void addProvider(final T provider) {
        ensureExists(provider);
    }

    /**
     * Update statistics of busy for provider
     *
     * @param provider as is
     */
    public void hitBusy(final T provider) {
        final Statistics stat = ensureExists(provider);

        if (stat.busyHits < 0) {
            stat.busyHits = 0;
        } else {
            stat.busyHits++;
        }

        Collections.sort(providers, comparator);
    }

    /**
     * Update statistics of idle for provider
     *
     * @param provider as is
     */
    public void hitIdle(final T provider) {
        final Statistics stat = ensureExists(provider);

        if (stat.busyHits > 0) {
            stat.busyHits--;
        }

        Collections.sort(providers, comparator);
    }

    /**
     * Update statistics of errors for provider
     *
     * @param provider as is
     * @return new count
     */
    public int hitError(final T provider) {
        return ++ensureExists(provider).errorHits;
    }

    /**
     * Get count error by provider
     *
     * @param provider as is
     * @return as is
     */
    public int getErrors(final T provider) {
        return statistics.containsKey(provider) ? statistics.get(provider).errorHits : 0;
    }

    /**
     * Reset error hits
     */
    public void resetErrors(final T provider) {
        ensureExists(provider).errorHits = 0;
    }

    /**
     * Get sorted list of provider sorted by usage from less to most busy
     *
     * @return as is
     */
    public List<T> listOfProviders() {
        return providers;
    }

    @Override
    public String toString() {
        return "GeoProviderUsage{" +
                "providers=" + providers +
                ", statistics=" + statistics +
                '}';
    }

    private static final class Statistics {
        private int busyHits = 0;
        private int errorHits = 0;

        @Override
        public String toString() {
            return "Statistics{" +
                    "busyHits=" + busyHits +
                    ", errorHits=" + errorHits +
                    '}';
        }
    }
}
